#!/usr/bin/env python
# -*- coding: utf-8 -*-

# graphicarea.py

# Copyright 2009 Diego Hansen Hahn (aka DiegoHH) <diegohh90 [at] hotmail [dot] com>

# lazynds is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License.

# lazynds is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with lazynds. If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import

import warnings
import os
import tempfile

import gtk
from gtk import gdk
import cairo
import gobject

#try:
#	from lazy import bitmap
#except:
#	import bitmap
from images import images

'''
 work in progress
 Reescrever!!
'''

__author__ = "Diego Hansen Hahn"
__version__ = "v2.0"

LEFT_BUTTON = 1
RIGHT_BUTTON = 3

# Bloquear o erro causado por usar altura e largura como float (linha 200)
warnings.simplefilter('ignore', DeprecationWarning)

# Bloquear ou não bloquear IOError, eis a questão....
# Em raríssimas ocasiões, o desenho do buffer não consegue acompanhar a velocidade de mudança da imagem,
# por isso, é levantando IOError, para avisar que o arquivo temporário não exite. - Diego
warnings.simplefilter('ignore', IOError)

class GraphicWidget(gtk.DrawingArea):

	__gsignals__ = {'realize' : 'override',
			'expose-event' : 'override',
			'button-press-event' : 'override',
			'button-release-event' : 'override',
			'motion-notify-event' : 'override',
			'scroll-event' : 'override'}

	def __init__(self):
		gtk.DrawingArea.__init__(self)

		self.add_events(gdk.BUTTON_MOTION_MASK | gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK)

		self.dx = 0
		self.dy = 0

		self.over = None
		self.dragging = None

		self.set_size_request(450,550)
		self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('white'))

	def add_container(self, container):
		self.container = container

	def do_expose_event(self, event):
		context = self.window.cairo_create()
		context.rectangle(event.area.x, event.area.y,
				  event.area.width, event.area.height)
		try:
			self.container.draw_context(context, self.dx, self.dy)
		except AttributeError:
			pass
		return False

	def do_realize(self):
		self.set_flags(self.flags() | gtk.REALIZED)

		events = gdk.EXPOSURE_MASK | gdk.BUTTON_PRESS_MASK | gdk.POINTER_MOTION_MASK

		self.window = gdk.Window(self.get_parent_window(),
						 x = self.allocation.x,
						 y = self.allocation.y,
						 width = self.allocation.width,
						 height = self.allocation.height,
						 window_type = gdk.WINDOW_CHILD,
						 wclass = gdk.INPUT_OUTPUT,
						 visual = self.get_visual(),
						 colormap = self.get_colormap(),
						 event_mask = self.get_events() | events)
		self.window.set_user_data(self)
		self.style.attach(self.window)
		self.style.set_background(self.window, gtk.STATE_NORMAL)

	def do_button_press_event(self, event):
		if event.button == LEFT_BUTTON:
			if self.over:
				self.container.unlock()
				self.dragging = self.over.get_position()
				self.dx = event.x - self.dragging[0]
				self.dy = event.y - self.dragging[1]


		return False

	def do_button_release_event(self, event):
		if event.button == LEFT_BUTTON:
			if self.dragging:
				self.dragging = None
				self.container.set_position()
				self.container.lock()
				self.refresh()

	def do_motion_notify_event(self, event):
		pos = (event.x, event.y)

		if self.dragging:
			self.over.set_position(pos)
			self.refresh()
			return False

		if self.container.is_mouse_over(pos):
			self.window.set_cursor(gdk.Cursor(gdk.HAND2))
			self.over = self.container
		else:
			self.over = None
			self.window.set_cursor(gdk.Cursor(gdk.TOP_LEFT_ARROW))

	def do_scroll_event(self, event):
		direction = event.direction

		ratio = self.container.get_scale()
		if direction == gdk.SCROLL_UP:
			if ratio < 3:
				ratio += 0.25
				self.container.set_scale(ratio)
				self.container.draw_image()
				self.refresh()
		if direction == gdk.SCROLL_DOWN:
			if ratio > 1:
				ratio -= 0.25
				self.container.set_scale(ratio)
				self.container.draw_image()
				self.refresh()
		return

	def refresh(self):
		alloc = self.get_allocation()
		rect = gdk.Rectangle(0, 0, alloc.width, alloc.height)
		self.window.invalidate_rect(rect, True)
		self.window.process_updates(True)
		return False

class ImageContainer(gobject.GObject):

	__gsignals__ = {'set-attr': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
								  (gobject.TYPE_PYOBJECT,))}

	@property
	def mode_1d(self):
		return 1
	@property
	def mode_2d(self):
		return 2

	def __init__(self, position):
		gobject.GObject.__init__(self)
		self.ratio = 1.0

		self.position = position
		self.x, self.y = self.position

		self.locker = True

		self.width = 0
		self.height = 0
		self.mode = self.mode_1d
		self.extra_blocks = 0

		self.tempfile = None
		self.transparent = False

		#gobject.signal_new('set-attr', ImageContainer, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))

	def __ne__(self, buffer):
		if hasattr(self, 'buffer'):
			return self.buffer.__ne__(buffer)
		else:
			return False

	def set_palette(self, palette):
		self.palette = palette

	def get_size(self):
		return (self.width, self.height)

	def set_buffer (self, buffer, width):
		if buffer:
			self.buffer = buffer
			self.width = width
		else:
			self.buffer = False

	def set_mode(self, mode):
		self.mode = mode

	def toggle_mode(self):
		if self.mode == self.mode_1d:
			self.set_mode(self.mode_2d)
		else:
			self.set_mode(self.mode_1d)

	def buffer2image(self):
		if hasattr(self, 'buffer'):
			if self.buffer:
				total = (len(self.buffer) * 8/self.palette.get_codec()) / 64
				if total % self.width == 0:
					self.height = total / self.width
				else:
					self.height = total / self.width + 1

				# Remove o temporário antigo, se existir
				if not isinstance(self.tempfile, type(None)):
					os.unlink(self.tempfile.name)

				self.tempfile = tempfile.NamedTemporaryFile(delete=False)
				self.tempfile.seek(0,0)
				w = images.Writer((self.width, self.height), self.palette.get_codec(), self.palette, self.transparent, self.mode)
				w.write(self.tempfile.file, self.buffer, 'PNG')
				self.tempfile.close()

				self.draw_image()
				self.emit('set-attr', self.extra_blocks)
			else:
				self.draw_image()

	def draw_image(self):
		if self.buffer:
			self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, (self.width+1) * 8 * self.ratio, (self.height+1) * 8 * self.ratio)
			context = cairo.Context(self.surface)
			pixbuf = gtk.gdk.pixbuf_new_from_file(self.tempfile.name)
			context = gtk.gdk.CairoContext(context)
			context.set_source_pixbuf(pixbuf.scale_simple(pixbuf.get_width() * self.ratio, pixbuf.get_height() * self.ratio, gdk.INTERP_BILINEAR), 8, 8)
		else:
			self.surface =  cairo.ImageSurface(cairo.FORMAT_ARGB32, 40, 40)
			context = cairo.Context(self.surface)
			pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(local_path, 'pixmaps/error.png'))
			context = gtk.gdk.CairoContext(context)
			context.set_source_pixbuf(pixbuf, 8, 8)
		context.paint()

	def draw_context(self, context, dx, dy):
		if self.locker == False:
			self.x, self.y = self.position
			self.x -= dx
			self.y -= dy
		context.set_source_surface(self.surface, self.x, self.y)
		context.paint()

	def unlock(self):
		self.locker = False

	def lock(self):
		self.locker = True

	def set_scale(self, ratio):
		self.ratio = ratio

	def get_scale(self):
		return self.ratio

	def set_position(self, pos = None):
		if pos == None:
			self.position = (self.x, self.y)
		else:
			self.position = pos

	def get_position(self):
		return self.position

	def is_mouse_over (self, pos):
		if (pos[0] > self.position[0] and pos[0] < (self.position[0] + 8 * self.width * self.ratio )) and \
		   (pos[1] > self.position[1] and pos[1] < (self.position[1] + 8 * self.height * self.ratio )):
			return True
		return False
